Skip to content

feat: allow cloud deletes for all object types (with name-based safety protections)#877

Merged
Aaron ("AJ") Steers (aaronsteers) merged 7 commits intomainfrom
aj/feat/mcp/allow-cloud-deletes-on-all-objects
Nov 27, 2025
Merged

feat: allow cloud deletes for all object types (with name-based safety protections)#877
Aaron ("AJ") Steers (aaronsteers) merged 7 commits intomainfrom
aj/feat/mcp/allow-cloud-deletes-on-all-objects

Conversation

@aaronsteers
Copy link
Copy Markdown
Member

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Nov 25, 2025

Summary

This PR adds safe mode deletion capabilities for all cloud object types (sources, destinations, connections, and custom source definitions) with name-based safety protections. Key changes:

  • Safe mode deletion: All delete operations now require the resource name to contain "delete-me" or "deleteme" (case insensitive) to prevent accidental deletions
  • Name verification: Delete operations require the caller to provide the expected name, which must match the actual resource name
  • New MCP tools: Added permanently_delete_cloud_source, permanently_delete_cloud_destination, and permanently_delete_cloud_connection endpoints
  • Simplified safety check: Removed the "delete:" prefix option; now only "delete-me" or "deleteme" patterns are accepted
  • Code cleanup: Removed try/except blocks in MCP cloud operations for cleaner error propagation

Updates since last revision

  • Resolved merge conflicts with main branch (integrated workspace_id parameter threading)
  • Applied ruff formatting fix to airbyte/_util/api_util.py
  • Updated integration test resource names to include "deleteme" suffix to comply with new safe mode deletion requirements

Review & Testing Checklist for Human

  • Verify safe mode logic: Test that deletion is blocked when resource name doesn't contain "delete-me" or "deleteme"
  • Verify name matching: Test that deletion fails when provided name doesn't match actual resource name
  • Test cascade deletions: Verify cascade_delete_source and cascade_delete_destination options work correctly with safe mode
  • Check workspace_id parameter: Verify the workspace_id parameter is correctly passed through all cloud operations after merge conflict resolution

Recommended test plan:

  1. Deploy a test source/destination/connection with a normal name and verify deletion is blocked
  2. Rename the resource to include "delete-me" and verify deletion succeeds
  3. Attempt deletion with wrong name and verify it fails with name mismatch error

Notes

  • The _is_safe_name_to_delete helper function is now shared across all deletion operations
  • Integration tests updated to reflect the simplified safety check (removed "delete:" prefix test case)
  • Test resource names updated to include "deleteme" suffix for proper cleanup

Requested by: Aaron ("AJ") Steers (@aaronsteers) (AJ Steers)
Devin session: https://app.devin.ai/sessions/9323978571ee49719d66f4b5f4005fa7


Summary by CodeRabbit

  • New Features

    • Introduced safe mode for permanent deletions (default on) requiring resource names to contain "delete-me" or "deleteme" (case-insensitive).
    • Added name-verified deletion utilities for cloud sources, destinations, connections and custom definitions; cascade deletions propagate safe mode.
  • Bug Fixes

    • Improved deletion safety checks and clearer, name-aware error messages when verification fails.
  • Documentation

    • Updated docstrings and user-facing messages to describe safe-mode behavior.
  • Tests

    • Adjusted integration tests and test data to match the new name-check behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

Testing This PyAirbyte Version

You can test this version of PyAirbyte using the following:

# Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@aj/feat/mcp/allow-cloud-deletes-on-all-objects' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@aj/feat/mcp/allow-cloud-deletes-on-all-objects'

Helpful Resources

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /fix-pr - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test-pr - Runs tests with the updated PyAirbyte

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances deletion safety for Airbyte Cloud resources by:

  • Removing the "delete:" prefix option for safe-mode deletions, now requiring only "delete-me" or "deleteme" in resource names
  • Adding name verification parameters to deletion operations to prevent accidental deletions
  • Introducing new deletion functions for sources, destinations, and connections with built-in safety checks
  • Removing try-except error handling blocks from deployment and operational functions

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/integration_tests/cloud/test_custom_definitions.py Updated test to reflect removal of "delete:" prefix option and verify new "deleteme" alternative
airbyte/mcp/cloud_ops.py Removed exception handling from deployment functions, added three new deletion functions with name-based safety protections, and fixed sync status return logic
airbyte/mcp/_tool_utils.py Added documentation clarifying safe mode behavior
airbyte/cloud/workspaces.py Added safe_mode parameters to deletion methods with updated documentation
airbyte/cloud/connectors.py Updated documentation to reflect new safe-mode naming requirements
airbyte/_util/api_util.py Implemented _is_safe_name_to_delete helper function, added safe_mode parameters to deletion functions, and added null-safety check for destination configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 25, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a safe_mode safety layer to deletion APIs requiring resource names to contain "delete-me" or "deleteme" (case-insensitive), propagates safe_mode and optional name args through workspace and MCP cloud deletion workflows, and refactors MCP cloud ops to remove broad try/except blocks and add explicit name verification on deletes.

Changes

Cohort / File(s) Change Summary
Core deletion API safety layer
airbyte/_util/api_util.py
Adds safe_mode: bool = True and optional *_name parameters to delete_source, delete_destination, delete_connection; introduces helper _is_safe_name_to_delete(name: str) -> bool; makes raw_configuration optional in get_destination; updates error contexts to reference resource names and raise PyAirbyteInputError when checks fail.
Workspace-level deletion workflows
airbyte/cloud/workspaces.py, airbyte/cloud/connectors.py
Adds safe_mode to permanently_delete_source, permanently_delete_destination, permanently_delete_connection; threads safe_mode and optional resource-name args to underlying API calls; updates docstrings and cascade-delete propagation.
MCP cloud operations refactoring
airbyte/mcp/cloud_ops.py
Removes broad try/except wrappers; adds explicit name verification and raises PyAirbyteInputError on mismatch; adds public deletion helpers (permanently_delete_cloud_source/destination/connection) requiring name verification; updates permanently_delete_custom_source_definition to accept name and verify; simplifies control flow and success messages.
MCP tooling docs
airbyte/mcp/_tool_utils.py
Docstring update: check_guid_created_in_session notes no-op when AIRBYTE_CLOUD_MCP_SAFE_MODE is "0"; no logic change.
Tests
tests/integration_tests/cloud/test_custom_definitions.py, tests/integration_tests/cloud/test_cloud_workspaces.py
Adjusts parametrized tests and expectations to align with new safe_name rule ("delete-me"/"deleteme"); updates integration tests to use -deleteme suffixes for deployed resource names and removes one parametrized case.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor CLI/Caller
    participant API as api_util.delete_*(id, name?, safe_mode)
    participant Validator as _is_safe_name_to_delete
    participant Backend as Airbyte Cloud API

    CLI/Caller->>API: request delete(id, safe_mode=True)
    rect rgb(247,250,245)
    Note over API,Validator: Safe-mode validation (name required or fetched)
    API->>API: if name not provided -> fetch resource name
    API->>Validator: validate name contains "delete-me" or "deleteme" (ci)
    Validator-->>API: valid / invalid
    end

    alt valid
        API->>Backend: DELETE /v1/.../{id}
        Backend-->>API: 200 OK
        API-->>CLI/Caller: success message (includes resource name)
    else invalid
        API-->>CLI/Caller: raise PyAirbyteInputError (includes name/context)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Attention recommended:
    • airbyte/mcp/cloud_ops.py — review removed broad try/except blocks for intended error propagation and CLI messaging.
    • Safe-mode propagation — ensure cascade-delete paths consistently pass safe_mode and resource-name args.
    • _is_safe_name_to_delete — verify case-insensitive matching and consistent usage across all delete callers.
    • Tests — ensure updated assertions and renamed resources match the new safety policy.

Would you like me to flag specific changed lines in airbyte/mcp/cloud_ops.py for closer inspection, wdyt?

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: introducing safe deletion for all cloud object types with name-based safety protections.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch aj/feat/mcp/allow-cloud-deletes-on-all-objects

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c351ede and b4dd9dd.

📒 Files selected for processing (1)
  • tests/integration_tests/cloud/test_cloud_workspaces.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/integration_tests/cloud/test_cloud_workspaces.py (5)
airbyte/cloud/connections.py (4)
  • name (86-94)
  • source (108-117)
  • destination (131-140)
  • CloudConnection (19-341)
airbyte/_connector_base.py (1)
  • name (84-86)
airbyte/secrets/env_vars.py (1)
  • name (37-41)
tests/integration_tests/cloud/conftest.py (2)
  • deployable_dummy_source (106-119)
  • deployable_dummy_destination (123-132)
airbyte/cloud/workspaces.py (2)
  • deploy_destination (195-248)
  • deploy_connection (318-366)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (No Creds)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (Fast)
🔇 Additional comments (2)
tests/integration_tests/cloud/test_cloud_workspaces.py (2)

21-21: LGTM! Resource names correctly follow the safe-mode deletion pattern.

All resource names now include the "-deleteme" suffix, which aligns perfectly with the safe-mode deletion requirements introduced in this PR. This ensures that the permanently_delete_* operations will succeed with the new name-based safety checks.

Also applies to: 41-41, 56-56, 71-71, 76-76, 82-82


86-86: Quick question about the table_prefix change - is this required?

The table_prefix was updated to "zzz_deleteme_" to include the "deleteme" pattern. Is this required by the deletion logic (e.g., does the cascade delete check table names), or is this just for consistency and clarity in the test? Just curious if there's a functional requirement here or if it's a nice-to-have, wdyt?


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
airbyte/_util/api_util.py (1)

994-1003: Consider simplifying the boolean expression, wdyt?

Using any({...}) with a set is a bit unconventional. A simple or expression would be more idiomatic and avoids the set construction overhead:

 def _is_safe_name_to_delete(name: str) -> bool:
     """Check if a name is safe to delete.
 
     Requires the name to contain either "delete-me" or "deleteme" (case insensitive).
     """
     name_lower = name.lower()
-    return any({
-        "delete-me" in name_lower,
-        "deleteme" in name_lower,
-    })
+    return "delete-me" in name_lower or "deleteme" in name_lower
airbyte/mcp/cloud_ops.py (3)

907-911: Comment mentions outdated naming pattern - mind updating it?

The inline comment references "delete:" which is not a valid safe-mode pattern anymore. Only "delete-me" or "deleteme" are accepted, wdyt?

     # Safe mode is hard-coded to True for extra protection when running in LLM agents
     workspace.permanently_delete_source(
         source=source_id,
-        safe_mode=True,  # Requires name-based delete disposition ("delete:" or "delete-me")
+        safe_mode=True,  # Requires name to contain "delete-me" or "deleteme"
     )

966-970: Same stale comment here too!

     # Safe mode is hard-coded to True for extra protection when running in LLM agents
     workspace.permanently_delete_destination(
         destination=destination_id,
-        safe_mode=True,  # Requires name-based delete disposition ("delete:" or "delete-me")
+        safe_mode=True,  # Requires name to contain "delete-me" or "deleteme"
     )

1044-1050: And here as well - third occurrence of the stale comment.

     # Safe mode is hard-coded to True for extra protection when running in LLM agents
     workspace.permanently_delete_connection(
-        safe_mode=True,  # Requires name-based delete disposition ("delete:" or "delete-me")
+        safe_mode=True,  # Requires name to contain "delete-me" or "deleteme"
         connection=connection_id,
         cascade_delete_source=cascade_delete_source,
         cascade_delete_destination=cascade_delete_destination,
     )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8cf82c and d5ba775.

📒 Files selected for processing (6)
  • airbyte/_util/api_util.py (7 hunks)
  • airbyte/cloud/connectors.py (1 hunks)
  • airbyte/cloud/workspaces.py (5 hunks)
  • airbyte/mcp/_tool_utils.py (1 hunks)
  • airbyte/mcp/cloud_ops.py (13 hunks)
  • tests/integration_tests/cloud/test_custom_definitions.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-06T23:44:31.534Z
Learnt from: aaronsteers
Repo: airbytehq/PyAirbyte PR: 411
File: airbyte/cli.py:111-160
Timestamp: 2024-10-06T23:44:31.534Z
Learning: In PyAirbyte, error messages in functions like `_resolve_source_job` in `airbyte/cli.py` are designed to decouple the message text from dynamic values, following a structlog-inspired design. Dynamic values are provided via parameters like `input_value`. This approach helps avoid including PII in the message strings, which may be used in telemetry.

Applied to files:

  • airbyte/mcp/cloud_ops.py
🧬 Code graph analysis (1)
airbyte/_util/api_util.py (1)
airbyte/exceptions.py (1)
  • PyAirbyteInputError (201-210)
🔇 Additional comments (13)
airbyte/mcp/_tool_utils.py (1)

51-52: LGTM!

The updated docstring accurately reflects the existing behavior - good addition to clarify when this check becomes a no-op.

airbyte/cloud/connectors.py (1)

503-504: LGTM!

Docstring accurately reflects the updated safe_mode naming requirements - consistent with the centralized _is_safe_name_to_delete helper.

airbyte/_util/api_util.py (5)

538-609: LGTM!

The safe_mode implementation is well-designed:

  • Lazy name fetching (only when needed)
  • Clear, actionable error messages
  • Appropriate context for debugging

747-761: LGTM!

Good defensive change - handling the optional raw_configuration prevents potential errors when the API response lacks a configuration field.


770-842: LGTM!

Consistent implementation with delete_source - same safe_mode pattern, error handling, and messaging.


1006-1078: LGTM!

Consistent safe_mode implementation across all three deletion functions. The pattern is clear and maintainable.


1468-1500: LGTM!

Properly uses the centralized _is_safe_name_to_delete helper and updated error messages align with the new naming requirements.

tests/integration_tests/cloud/test_custom_definitions.py (1)

166-166: LGTM!

The assertion correctly validates that the error message mentions the valid safe-mode naming patterns.

airbyte/cloud/workspaces.py (3)

250-278: LGTM!

Good optimization - passing source_name when available avoids an extra API call in the deletion path when safe_mode is enabled.


282-314: LGTM!

Consistent pattern with permanently_delete_source - properly passes destination_name to avoid redundant fetches.


368-415: LGTM!

Nice implementation - safe_mode is properly propagated to cascade deletions, ensuring consistent behavior for the entire deletion tree.

airbyte/mcp/cloud_ops.py (2)

11-11: LGTM!

Appropriate import for input validation in the MCP layer.


833-853: Nice defense-in-depth approach!

The MCP layer adds name verification on top of the API's safe_mode check - two independent safety mechanisms that both must pass. This is appropriate protection when LLM agents are making destructive calls.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI added a commit that referenced this pull request Nov 25, 2025
…h safety protections

Co-authored-by: aaronsteers <18150651+aaronsteers@users.noreply.github.com>
Copilot AI added a commit that referenced this pull request Nov 26, 2025
Co-authored-by: aaronsteers <18150651+aaronsteers@users.noreply.github.com>
…lve conflicts

Co-Authored-By: AJ Steers <aj@airbyte.io>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
airbyte/mcp/cloud_ops.py (1)

1040-1091: Same workspace_id omission as source deletion.

This function follows the same pattern as permanently_delete_cloud_source and also omits the workspace_id parameter. If you decide to add it to the source function, consider adding it here too for consistency.

🧹 Nitpick comments (2)
airbyte/mcp/cloud_ops.py (2)

981-1032: Missing workspace_id parameter - is this intentional?

I noticed this function doesn't accept a workspace_id parameter, unlike permanently_delete_custom_source_definition (line 924-930) and other similar functions like rename_cloud_source. This means it always uses the default workspace from environment variables.

Was this a deliberate simplification, or should it include the optional workspace_id parameter for consistency with the rest of the API, wdyt?

 def permanently_delete_cloud_source(
     source_id: Annotated[
         str,
         Field(description="The ID of the deployed source to delete."),
     ],
     name: Annotated[
         str,
         Field(description="The expected name of the source (for verification)."),
     ],
+    *,
+    workspace_id: Annotated[
+        str | None,
+        Field(
+            description="Workspace ID. Defaults to AIRBYTE_CLOUD_WORKSPACE_ID env var.",
+            default=None,
+        ),
+    ] = None,
 ) -> str:

1099-1171: Cascade delete options look good, same workspace_id note applies.

The cascade delete options (cascade_delete_source, cascade_delete_destination) are well-structured with clear Field descriptions. The safe_mode=True is correctly passed to the underlying call.

Same note about missing workspace_id parameter applies here. The consistency across all three new delete functions suggests this might be intentional though?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f92a03 and ae3708e.

📒 Files selected for processing (2)
  • airbyte/mcp/_tool_utils.py (1 hunks)
  • airbyte/mcp/cloud_ops.py (14 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • airbyte/mcp/_tool_utils.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-06T23:44:31.534Z
Learnt from: aaronsteers
Repo: airbytehq/PyAirbyte PR: 411
File: airbyte/cli.py:111-160
Timestamp: 2024-10-06T23:44:31.534Z
Learning: In PyAirbyte, error messages in functions like `_resolve_source_job` in `airbyte/cli.py` are designed to decouple the message text from dynamic values, following a structlog-inspired design. Dynamic values are provided via parameters like `input_value`. This approach helps avoid including PII in the message strings, which may be used in telemetry.

Applied to files:

  • airbyte/mcp/cloud_ops.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (No Creds)
🔇 Additional comments (5)
airbyte/mcp/cloud_ops.py (5)

131-153: Clean refactoring of deploy operations.

The removal of broad try/except blocks in favor of direct execution paths is a good improvement. Errors will now propagate with meaningful stack traces instead of being potentially swallowed or repackaged.


462-495: Solid null handling and structured response.

The explicit check for missing sync_result with a clear fallback response is a nice improvement. The structured dictionary response is clean and well-organized.


640-673: Thorough edge-case handling for log retrieval.

All the null/empty cases are handled with clear, descriptive messages. The attempt lookup logic correctly iterates to find the specified attempt or falls back to the latest one.


953-973: Good safety pattern with name verification.

The name mismatch check with detailed context in the exception is a solid safety mechanism. The strict case-sensitive comparison is appropriate for preventing accidental deletions.


1010-1010: Cast assumes name is always present.

The cast(str, source.name) assumes the API always returns a non-null name for deployed sources. This is probably safe in practice, but if there's ever a case where name is None, the subsequent comparison would fail unexpectedly.

Is this assumption documented or guaranteed by the API contract? If not, you might consider a defensive check, wdyt?

actual_name: str = source.name or ""
if not actual_name:
    raise PyAirbyteInputError(
        message="Source has no name, cannot verify for deletion.",
        context={"source_id": source_id},
    )

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 27, 2025

PyTest Results (Fast Tests Only, No Creds)

320 tests  ±0   320 ✅ ±0   5m 57s ⏱️ -6s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit b4dd9dd. ± Comparison against base commit a8cf82c.

♻️ This comment has been updated with latest results.

Co-Authored-By: AJ Steers <aj@airbyte.io>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
airbyte/_util/api_util.py (3)

747-757: Consider tightening the type check on raw_configuration before splatting

raw_configuration is now optional, which is great, but we still assume it’s a mapping when we do **raw_configuration. Would it be worth guarding with an isinstance to avoid a surprising TypeError if the API ever returns a non-dict configuration (e.g., None or a list) for a known destination type, wdyt?

-        raw_configuration: dict[str, Any] | None = raw_response.get("configuration")
+        raw_configuration: dict[str, Any] | None = raw_response.get("configuration")
@@
-        if destination_type in destination_mapping and raw_configuration is not None:
-            response.destination_response.configuration = destination_mapping[
-                destination_type  # pyrefly: ignore[index-error]
-            ](**raw_configuration)
+        if (
+            destination_type in destination_mapping
+            and isinstance(raw_configuration, dict)
+        ):
+            response.destination_response.configuration = destination_mapping[
+                destination_type  # pyrefly: ignore[index-error]
+            ](**raw_configuration)

994-1005: Simplify _is_safe_name_to_delete for readability

The helper behaves correctly, but the any({ ... }) with a set literal is a bit unusual for a simple OR check. Would simplifying this to a direct boolean expression help future readers scan it faster, wdyt?

 def _is_safe_name_to_delete(name: str) -> bool:
@@
-    name_lower = name.lower()
-    return any(
-        {
-            "delete-me" in name_lower,
-            "deleteme" in name_lower,
-        }
-    )
+    name_lower = name.lower()
+    return "delete-me" in name_lower or "deleteme" in name_lower

1470-1495: Minor readability tweak and potential DRY opportunity in custom YAML delete

The safe_mode behavior for custom YAML source definitions matches the other delete paths, which is great. Two tiny thoughts:

  1. Since you already assign connector_name = definition_info.name, would calling _is_safe_name_to_delete on connector_name read a bit cleaner?
  2. Given that the safe_mode blocks and error messages are now very similar across sources, destinations, connections, and custom definitions, would a small internal helper (e.g., _ensure_safe_delete(resource_type, name, id, safe_mode)) help reduce duplication and keep future message changes in one place, wdyt?
-        connector_name = definition_info.name
-
-        if not _is_safe_name_to_delete(definition_info.name):
+        connector_name = definition_info.name
+
+        if not _is_safe_name_to_delete(connector_name):
             raise PyAirbyteInputError(
                 message=(
                     f"Cannot delete custom connector definition '{connector_name}' "
                     "with safe_mode enabled. "
                     "To authorize deletion, the connector name must contain 'delete-me' or "
                     "'deleteme' (case insensitive).\n\n"
                     "Please rename the connector to meet this requirement "
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae3708e and c351ede.

📒 Files selected for processing (1)
  • airbyte/_util/api_util.py (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
airbyte/_util/api_util.py (3)
airbyte/sources/util.py (1)
  • get_source (47-139)
airbyte/exceptions.py (1)
  • PyAirbyteInputError (201-210)
airbyte/destinations/util.py (1)
  • get_destination (21-95)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (No Creds)
🔇 Additional comments (3)
airbyte/_util/api_util.py (3)

541-591: Safe-mode guard for delete_source looks consistent and clear

The safe_mode flow (optional source_name, fetch-if-missing, centralized _is_safe_name_to_delete check, and a detailed PyAirbyteInputError) looks solid and matches the intent described in the docstring. I don’t see any functional issues here, and the context payload on the error should be helpful for callers, wdyt?


773-824: Destination safe-mode deletion behavior matches source semantics

The delete_destination safe_mode flow mirrors delete_source nicely (optional destination_name, fetch on demand, centralized helper, and a descriptive PyAirbyteInputError). The consistency across these two entry points should make behavior predictable for callers; I don’t see any issues here, wdyt?


1009-1061: Connection safe-mode deletion is consistent with sources/destinations

The delete_connection signature and implementation follow the same safe_mode pattern (including fetching connection_name when needed and raising PyAirbyteInputError with good context), which keeps the API surface uniform across object types. Functionally this looks good to me; I don’t see any additional edge cases you need to handle right now, wdyt?

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 27, 2025

PyTest Results (Full)

388 tests   - 1   372 ✅  - 1   23m 56s ⏱️ - 3m 3s
  1 suites ±0    16 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit b4dd9dd. ± Comparison against base commit a8cf82c.

This pull request removes 1 test.
tests.integration_tests.cloud.test_custom_definitions ‑ test_safe_mode_deletion[delete:test-yaml-source-{suffix}-True]

♻️ This comment has been updated with latest results.

@aaronsteers Aaron ("AJ") Steers (aaronsteers) merged commit f7ba17f into main Nov 27, 2025
24 checks passed
@aaronsteers Aaron ("AJ") Steers (aaronsteers) deleted the aj/feat/mcp/allow-cloud-deletes-on-all-objects branch November 27, 2025 04:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants